package com.zx.auth.block.component;


import com.alibaba.fastjson2.JSONObject;
import com.zx.auth.block.constant.Constants;
import com.zx.core.wrapper.ObjectWrapper;
import com.zx.core.wrapper.ResponseWrapper;
import com.zx.feign.client.SystemClient;
import com.zx.redis.block.service.RedisBlockService;
import com.zx.security.common.model.LoginUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * description: 登录服务 <br>
 * create: 2024-08-24 13:41
 * </p>
 *
 * @author zhou  xun
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LoginService {
    // 登录过期时间（秒）
    private static final long LOGIN_EXPIRE_SECONDS = 60 * 60 * 24 * 7L; // 7天

    private final AuthenticationManager authenticationManager;
    private final JwtEncoder jwtEncoder;
    private final RedisBlockService redisBlockService;
    private final SystemClient systemClient;

    /**
     * 登录验证
     *
     * @param username 用户名
     * @param password 密码
     * @param code     验证码
     * @param uuid     唯一标识
     * @author zhou xun
     * @since 2024-08-24
     */
    public ObjectWrapper<JSONObject> login(String username, String password, String code, String uuid) {
        ObjectWrapper<JSONObject> result = new ObjectWrapper<>();

        // 验证码校验
        ResponseWrapper captchaResult = checkCaptcha(code, uuid);
        if (captchaResult.verifyFail()) {
            result.fail(captchaResult.getMsg());
            return result;
        }

        // 用户验证
        Authentication authentication;
        try {
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (Exception e) {
            log.error("Authentication failed for user: {}", username, e);
            result.fail("用户验证失败");
            return result;
        }

        LoginUser loginUser = (LoginUser) authentication.getPrincipal();

        // 生成token
        Set<String> authorities = loginUser.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toSet());

        Instant now = Instant.now();
        JwtClaimsSet jwtClaimsSet = JwtClaimsSet.builder()
                .subject(authentication.getName())
                .issuer("zx-auth-service")
                .issuedAt(now)
                .claim("userId", loginUser.getId())
                .claim("userName", loginUser.getUsername())
                .claim("authorities", authorities)
                .claim("tenantId", loginUser.getTenantId())
                .claim("tenantName", loginUser.getTenantName())
                .expiresAt(now.plusSeconds(LOGIN_EXPIRE_SECONDS))
                .build();

        String token;
        try {
            token = jwtEncoder.encode(JwtEncoderParameters.from(jwtClaimsSet)).getTokenValue();
        } catch (Exception e) {
            log.error("JWT 编码失败", e);
            result.fail("token生成失败");
            return result;
        }
        if (StringUtils.isBlank(token)) {
            log.warn("生成的 token 为空");
            result.fail("token生成失败");
            return result;
        }

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("access_token", token);
        jsonObject.put("expires_in", LOGIN_EXPIRE_SECONDS);
        result.setData(jsonObject);
        return result;
    }

    /**
     * 验证码校验
     *
     * @param code 验证码
     * @param uuid 唯一标识
     * @author zhou xun
     * @since 2024-08-24
     */
    private ResponseWrapper checkCaptcha(String code, String uuid) {
        ResponseWrapper result = new ResponseWrapper();

        if (StringUtils.isEmpty(uuid) || StringUtils.isBlank(code)) {
            result.fail("uuid或验证码不能为空");
            return result;
        }

        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
        String captcha;
        try {
            captcha = redisBlockService.getCacheObject(verifyKey);
        } catch (Exception e) {
            log.error("获取验证码失败，uuid: {}", uuid, e);
            result.fail("验证码校验失败");
            return result;
        }

        if (captcha == null) {
            result.fail("验证码已失效");
            return result;
        }

        if (!captcha.equalsIgnoreCase(code.trim())) {
            result.fail("验证码错误");
            return result;
        }

        try {
            redisBlockService.deleteObject(verifyKey);
        } catch (Exception e) {
            log.warn("验证码删除失败，key: {}", verifyKey, e);
        }

        return result;
    }
}